tree grammar TreeWalker;

options {
  language = Java;
  tokenVocab = Cuda;
  ASTLabelType = CommonTree;
  output=AST;
  backtrack=true;
}


@header {
	package cuda;
	import java.lang.System;
	import cuda.translation.*; 
	import cuda.translation.ITransRules.*;
	import org.antlr.runtime.RecognitionException;
}

@members {
	
	public Map<String, Variable> vars = new HashMap<String, Variable>();
	     
  	Boolean breakAvailable = false;
 	void print(String msg)		{	System.out.print(msg);	}
 	void println(String msg)	{	System.out.println(tab + msg);	}
	String tab = "";
	void ident()				{	tab += "\t";	}
	void deident()				{	tab = tab.substring(1);	}
	
	String call(OpType type, String []args) {
		return call( Configuration.translator.getFunctionName(type), args);
	}
	
	String call(String functionName, String []args){
		String code = functionName + "(";
		boolean first = true;
		if(args!=null)
			for(String arg : args){
				if(!first) code += ", ";
				else first = false;
				code += arg;
			}
		code += ");";
		return code;
	}
	

	
	Type oType(Type left, Type right, String operation)throws SemanticException{
		OpHandler handler = OperMap.get(left, right, operation);
		if(handler == null) ErrorHandler.report(String.format("Operator \%s cannot be used with types \%s \%s", operation, left, right));
		return handler.returnType;
	}
	
	
	String oFunc(Type left, Type right, String operation)throws SemanticException{
		OpHandler handler = OperMap.get(left, right, operation);
		if(handler == null) ErrorHandler.report(String.format("Operator \%s cannot be used with types \%s \%s", operation, left, right));
		return Configuration.translator.getFunctionName(handler.operationType);
	}
	
	String oCall(TreeWalker.expression_return l, TreeWalker.expression_return r, String o)throws SemanticException{
		 if(l.type == Type.SCALAR && r.type == Type.SCALAR) return String.format("(\%s \%s \%s)", l.code, o, r.code);
		 return call(oFunc(l.type, r.type, o), new String[]{l.code, r.code});
	}
	
	void assertType(Type t1, Type t2)throws SemanticException{
		if(t1 != t2) ErrorHandler.report("Uncompatibile types conversion. Cannot cast type " + t1.toString() + " to " + t2.toString());
	}
	
	void assertDeclared(String var)throws SemanticException{
		if(!vars.containsKey(var)) ErrorHandler.report("Undefined variable " + var);
	}
	
	void assertNotDeclared(String var) throws SemanticException{
		if(vars.containsKey(var)) ErrorHandler.report("Variable " + var + " already defined");
	} 

	String closing = String.format("\%s\%s\n\%sreturn 0;\n}\n\n", tab, call(OpType.SHUTDOWN, null), tab);
	String init = String.format("#include \%s\n\n" +
				"int main(){\n\%s\%s\n", Configuration.translator.getHeaderFileName(),tab,call(OpType.INIT, null));
}

@init{
	//CZEMU DO KURWY TO SIE NIE ODPALA?

}

@rulecatch {
	catch(RecognitionException err) {
		throw err;
	}
}

program returns [String code, String app] 
	:  seq=statementSequence EOF	
		{	retval.app = init + seq.code + closing;
			retval.code = seq.code;
		 }
	;
	

	

	
statementSequence returns [String code = ""]  
	: ^(STMT_SEQ (stat=statement { retval.code += tab + stat.code + "\n";} )* ) 
	;



statement returns [String code]  
	:	d=declarationStatement		{retval.code = d.code;}
	|	a=assignmentStatement		{retval.code = a.code;}
	|	i=ifStatement				{retval.code = i.code;}
	|	f=forStatement				{retval.code = f.code;}
	|	w=whileStatement			{retval.code = w.code;}
	|	s=switchStatement			{retval.code = s.code;}
	|	'break'	 			{if(!breakAvailable) ErrorHandler.report("illegal break statement!");
								retval.code = "break;";}
	|	'continue'			{if(!breakAvailable) ErrorHandler.report("illegal continue statement!");
								retval.code = "continue;";}
	;

	
	//TODO: Deklaracja kilku pod rzad?
declarationStatement returns [String code] 
	:	^('scalar' id_s=IDENT val_s=r_value?)
			{ 	assertNotDeclared(id_s.getText());
				retval.code = "float " + id_s.getText();	
				if(val_s != null){
					assertType(val_s.type, Type.SCALAR);
					retval.code += " = " + val_s.code;
					vars.put(id_s.getText(), new Variable(Type.SCALAR, true));
				}
				else vars.put(id_s.getText(), new Variable(Type.SCALAR, false));
				retval.code += ";";
			}
	
	|	^('vector' id_v=IDENT (size=expression | val_v=r_value)?)	
			//	if(dimv.type != Type.SCALAR) ErrorHandler.report("Wrong dimension type: " + dimv.type.toString() + " Integer expected."); 
			{	assertNotDeclared(id_v.getText());
				retval.code = "Vector *" + id_v.getText();
				if(size != null){
				 	retval.code += " = new Vector(" + size.code + ")";
				 	vars.put(id_v.getText(), new Variable(Type.VECTOR, true));
				 }
				 else vars.put(id_v.getText(), new Variable(Type.VECTOR, false));
				 retval.code += ";";
				 
				 if(val_v != null){
				 	assertType(val_v.type, Type.VECTOR);
				 	retval.code += "\n" + tab + call(OpType.VECTOR_COPY, new String[]{val_v.code, id_v.getText()});	 
				 }
			}
	|   ^('matrix' id_m=IDENT cols=expression rows=expression val_m=r_value?)
			{	assertNotDeclared(id_m.getText());
				retval.code = "Matrix *" + id_m.getText();
				if(cols != null && rows != null){
				 	retval.code += " = new Matrix(" + cols.code + ", " + rows.code + ")";
				 	vars.put(id_m.getText(), new Variable(Type.MATRIX, true));
				 }
				 else vars.put(id_m.getText(), new Variable(Type.MATRIX, false));
				 retval.code += ";";
				 
				 if(val_m != null) {
				 	assertType(val_m.type, Type.MATRIX);
				 	retval.code += "\n" + tab + "initialization_matrix;";
				 }
			}
	;

/**	Regula dla postawien. W szczegolnosci, dzieki opcjonalnej drugie czesci, regula ta umozliwia samo wywolywanie funkcji 
*	Jesli jest to wywolanie, to drzewo AST bedzie takie, jak przy wywolaniach (patrz call)
*	Jesli to jest przypisanie, drzewo AST ma postac:
*	(= l_value r_value)
*/
assignmentStatement returns [String code] 
	:	^('=' id=l_value val=r_value) 
			{	
				assertType(id.type, val.type);
				if(id.type == Type.SCALAR) retval.code = id.code + " = " + val.code + ";";
				else retval.code = call(oFunc(id.type, val.type, "="), new String[]{id.code, val.code});
			}
	;	
	

//IF STATEMENT
ifStatement returns [String code] 
	: ^('if' cond=expression iseq=statementSequence elsi=elseIfStatements els=elseStatement?)
		{retval.code = "if (" + cond.code + "){\n" + iseq.code + tab + "}\n"; 
			if(elsi != null) retval.code +=elsi.code;
			if(els != null) retval.code += els.code;
		}
	
	;

elseIfStatements returns [String code=""] 
	:	(stat=elseIfStatement {retval.code += stat.code;})*
	;	

elseIfStatement returns [String code] 
	: ^('elseif' cond=expression seq=statementSequence) {retval.code = tab + "else if(" + cond.code + ") {\n" + seq.code + tab + "}\n";}
	;

elseStatement returns [String code] 
	: ^('else' seq=statementSequence) {retval.code = tab + "else {\n" + seq.code + tab + "}"; }
	;

//FOR STATEMENT 
forStatement returns [String code] 
	:	^('for' id=IDENT val=r_value ba seq=statementSequence be) 
			{	String f_vec = "_FOR_" + id.getText(); String f_vec_i = "_FOR_I_" + id.getText();
				retval.code = String.format("Vector \%s = \%s;\n"+
					"\%sfor(int \%s = 0, float \%s = \%s.get(0); \%s < \%s.getSize(); \%s++, \%s = \%s.get(\%s)){\n\%s\%s}",
					f_vec, val.code, tab, f_vec_i, id.getText(), f_vec, f_vec_i, f_vec, f_vec_i, id.getText(),
					f_vec, f_vec_i, seq.code, tab);
			}
	;
/*
forCondition returns [String code] 
	:	(id=l_value beg=term ':' (step=term ':')? end=term)
			{retval.code = "float " + id.code + " = " + beg.code + "; ";
			retval.code +=  id.code + " <= " + end + "; ";
			retval.code += id.code + " += " + ((step == null) ? 1 : step.code); }
	;*/

//WHILE STATEMENT
whileStatement returns [String code] 
	:	^('while' e=expression ba seq=statementSequence be)
		{retval.code = "while (" + e.code +"){\n" + seq.code + tab + "}";}
	;
	
ba 
	: {breakAvailable = true;}
	;
	
be
	: {breakAvailable = false;}
	;
	
//SWITCH STATEMENT
switchStatement returns [String code] 
	:	^('switch' expression ('case' expression expression* ba statementSequence be)* ('otherwise' statementSequence)?)
	;
	
	// TODO: 
		// function definition - jak to zrobic, w matlabie robi sie w osobnych plikach 
	
		
/* ****************** VALUES ********************** */
/* Ponizsze reguly opisuja rozne rodzaje typow, wystepujacych w wyrazeniach */

/*	Odwolania do vectorow i macierzy maja taka sama skladnie jak wywolania funkcji, np. A(2,3) 
*	Zwracane dla nich drzewo AST ma postac (CALL IDENT arg+)
*/
l_value returns [Type type, float val, String code] 
	:/*	^(CALL IDENT argumentList)
	|*/	  id=IDENT	
		{	assertDeclared(id.getText());
			retval.type = vars.get(id.getText()).type;
			retval.code = id.getText();
		}
	;
	
/**	Moga to byc zwykle wyrazenia, albo listy w postaci np 1:2, 1:x:3.
*	Dla list zwracane sa odpowiednio drzewa AST:
*	(LIST 1 2)
*	(DLIST 1 2 3)
*/
	
r_value returns [Type type, float val, String code]  
	:	^(LIST beg=expression step=expression end=expression)	
		{	retval.type = Type.VECTOR;
			retval.code = "(new Vector(" + beg.code + ", " + (step != null ?  step.code : 1) + ", " + end.code + "))";
		}
	//|	^(LIST expression expression) 	{retval.type = Type.LIST;}
	|	e=expression					{retval.type = e.type; retval.code = e.code;}
	;




/* 	':' Wystepuje w odwolaniach do wektorow/macierzy, oznacza wybranie calego zakresu
*	http://www.mathworks.com/access/helpdesk/help/techdoc/ref/colon.html */
argumentList 
	:	(expression | ':') (','! (expression | ':'))*
	;
		
scalar	returns [Type type, float val, String code]	 //przyszlosciowo bo uzywane tylko floaty
	: 	it=INTEGER			{retval.type = Type.SCALAR; retval.code = it.getText(); retval.val = Float.parseFloat(it.getText());}
	|	fl=FLOAT			{retval.type = Type.SCALAR; retval.code = fl.getText(); retval.val = Float.parseFloat(fl.getText());}
	;
	
term	returns [Type type, float val, String code] 
	:	l=l_value		{retval.type= l.type; retval.code = l.code;}
	|	s=scalar		{retval.type= s.type; retval.code = s.code;}
	;
		
/******************* EXPRESSIONS ***********************/
/*	Ponizsze reguly zwracaja klasyczne drzewa syntaktyczne dla wyrazen arytmetyczno-logicznych
*	Korzeniami podrzew sa same operatory, z wyjatkiem minusa unarnego, gdzie korzeniem jest
*	wirtualny token NEGATION.
*
*	Obslugiwane operatory:
*		* arytmetyczne: ^, .^, unarny -, unarny +, *, .*, /, ./, +, -
*		* relacyjne: ==, ~=, <, <=, >, >=
*		* logiczne: &&, ||, ~
*
*	Opracowano na podstawie http://www.mathworks.com/access/helpdesk/help/techdoc/matlab_prog/f0-40063.html
*
*	Uwaga: A.+B jest traktowane jako (.+ A B), natomiast w przypadku skalarow
*	2.+B jest traktowane jako (+ 2.0 B). Nie jest to jednak ograniczenie, gdyz w przypadku skalarow
*	operacje + i .+ sa tozsame.
*/

/* 	Troche bardziej skomplikowana regula. Buduje prawostronne drzewa, np. (^ 2 (^ 2 2)),
*	poniewaz tak wiaze sie potegowanie.
*	Pozostale reguly buduja lewostronne drzewa, np. (+ (+ 2 2) 2)
*/


//TODO: Czy dopuscic kilka pod rzad? Po co?
unary  returns [Type type, String code] 
	:	^( op=('+' | '-' | '~' | '^' | '.^') e=expression) 
				{	assertType(e.type, Type.SCALAR);
					retval.type = e.type;
					retval.code = op.getText() + e.code;}
	;
	/*
negation //returns [String text]
	:	NEGATION //{retval.text = "-";}
	;*/
/*
mult  returns [Type type, String code] 
	:	^( op=('*' | '/' | '.*'| './') e1=expression e2=expression) 
		{	if(e1.type == Type.SCALAR || e1.type == Type.SCALAR){
				retval.type = Type.SCALAR; 
				retval.code = e1.code + " " + op.getText() + " " + e2.code;
			}
			else {
				retval.code = call(oFunc(e1.type, e2.type, op.getText()), new String[]{e1.code, e2.code});
				retval.type = oType(e1.type, e2.type, op.getText());
			}		
		}  
	;	
	*/
add returns [Type type, String code] 
	: 	^( op=('+' | '-'| '*' | '/' | '.*'| './') e1=expression e2=expression) 
			{	if(e1.type == Type.SCALAR || e1.type == Type.SCALAR){
					retval.type = Type.SCALAR; 
					retval.code = e1.code + " " + op.getText() + " " + e2.code;
				}
				else {
					retval.code = call(oFunc(e1.type, e2.type, op.getText()), new String[]{e1.code, e2.code});
					retval.type = oType(e1.type, e2.type, op.getText());
				}		
			}
	;
	
relation returns [Type type, String code] 
	:   ^( op=('==' | '~=' | '<' | '<=' | '>'  | '>=' | '&&' | '||') e1=expression e2=expression)
			{	if(e1.type == Type.SCALAR || e1.type == Type.SCALAR){
					retval.type = Type.SCALAR; 
					retval.code = e1.code + " " + op.getText() + " " + e2.code;
				}
				else {
					retval.code = call(oFunc(e1.type, e2.type, op.getText()), new String[]{e1.code, e2.code});
					retval.type = oType(e1.type, e2.type, op.getText());
				}		
			}
	;
 /*
binary returns [Type type, String code] 
 	: 	^(op=('&&' | '||') e1=expression e2=expression )
 			{retval.type = Type.BINARY;
 			 retval.code = e1.code + " " + op.getText() + " " + e2.code;}
 	;*/
	
//TODO: Czy zaimplementowac operatory '&' i '|', ktore operuja na el. nieskalarnych i generuja wynik ?

expression returns [Type type, float val, String code] 
	:	e1=unary	{retval.type = e1.type; retval.code = "(" + e1.code + ")";}
	//|	e2=mult		{retval.type = e2.type; retval.code = "(" + e2.code + ")";}
	|	e3=add		{retval.type = e3.type; retval.code = "(" + e3.code + ")";}
	|	e4=relation	{retval.type = e4.type; retval.code = "(" + e4.code + ")";}
	//|	e5=binary	{retval.type = e5.type; retval.code = "(" + e5.code + ")";}
	|	e6=term  	{retval.type = e6.type; retval.code =  e6.code;}
	;

	